home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 22 / Cream of the Crop 22.iso / os2 / blt2_212.zip / src / compact.c < prev    next >
C/C++ Source or Header  |  1996-11-06  |  17KB  |  592 lines

  1.  
  2. /* compact.c -  5-Nov-1996 Cornel Huth
  3.  * This module is an -example- technique, showing one method to compact a database (DBF and DBT).
  4.  * Any index file(s) for this data file must be reindexed after this compaction is run.  This
  5.  * is for a DBF with DBT memo file attached.  For just DBFs, use PACK_RECORDS_XB, instead.
  6.  *
  7.  * Input consists of two filenames: dbfFilenamePtr is the current DBF filename
  8.  * (must not be open, though could be if this source were modified), and
  9.  * dbfNewFilenamePtr the name to give the compacted data file (.DBF).  Output
  10.  * is the dbfNewFilenamePtr files (both DBF and DBT), compacted by removing both
  11.  * deleted data records along with the memo records that belonged to those
  12.  * deleted data records.  It's not necessary to compact a memo file since any
  13.  * deleted space is made available for re-use, but this technique may come in
  14.  * handy anyway.
  15.  *
  16.  * #define TEST_COMPACT to compile test routine at end of this module
  17.  *
  18.  */
  19.  
  20. // platform.h again used to simplify construction for various platforms
  21. // FOR_WINDOWS should not be used since this example uses printf() for output
  22.  
  23. #define TEST_COMPACT    // define if main() test wanted (at end of this source)
  24.  
  25.  
  26. #include "platform.h"           // defines platform, brings in includes
  27.  
  28.  
  29. LONG compactDB(CHAR *dbfFilenamePtr, CHAR *dbfNewFilenamePtr) {
  30.  
  31. #pragma pack(1)
  32.  
  33.  ACCESSPACK APo;  // for old files
  34.  ACCESSPACK APn;  // for new files
  35.  COPYPACK CP;
  36.  DESCRIPTORPACK DP;
  37.  HANDLEPACK HP;
  38.  MEMODATAPACK MDPo;
  39.  MEMODATAPACK MDPn;
  40.  OPENPACK OP;
  41.  STATDATAPACK SDP;
  42.  
  43. #pragma pack()
  44.  
  45.  int i,j;
  46.  LONG rez;
  47.  CHAR tmpStr[128];
  48.  ULONG deletedRecs=0;
  49.  
  50.  ULONG recs=0;
  51.  ULONG dbfHandleOld=0;
  52.  ULONG dbfHandleNew=0;
  53.  
  54.  CHAR *dbfBufferPtr=NULL;
  55.  
  56. #define MAX_LIKELY_MEMO_SIZE 96  // max likely memo data size, in KB, for memoBufferPtr
  57. #define MAX_MEMO_FIELDS 128      // max memo fields per data record (1 or 2 is typical)
  58.  
  59.  int memoMaxSize = MAX_LIKELY_MEMO_SIZE * 1024;
  60.  int memoFields=0;               // number of memo fields in data record
  61.  ULONG memoNo;                   // memo number, binary (for scanf)
  62.  ULONG memoFO[MAX_MEMO_FIELDS]={0}; // offset start of each memo field in record
  63.  VOID *memoBufferPtr=NULL;       // memo I/O buffer
  64.  
  65.  // open DBF/DBT files
  66.  
  67.  OP.func = OPEN_DATA_XB;
  68.  OP.filenamePtr = dbfFilenamePtr;
  69.  OP.asMode = READWRITE | DENYREADWRITE;
  70.  rez = BULLET(&OP);
  71.  if (rez) {
  72.     printf("DBF open failed, rez: %d\n",rez);
  73.     return 0;
  74.  }
  75.  
  76.  dbfHandleOld = OP.handle;
  77.  
  78.  // get stats on data file handle
  79.  // goto used to simplify flow structure on error exits
  80.  
  81.  SDP.func = STAT_DATA_XB;
  82.  SDP.handle = dbfHandleOld;
  83.  rez = BULLET(&SDP);
  84.  if (rez==0) {
  85.  
  86.     if (SDP.records == 0) {
  87.        printf("DBF has no records\n");
  88.        goto ExitNow;
  89.     }
  90.     if (SDP.memoHandle == 0) {
  91.        printf("DBF has no DBT memo file attached\n");
  92.        // may choose to do a PACK_RECORDS_XB here, then exit
  93.        goto ExitNow;
  94.     }
  95.  
  96.     // determine memo field locations in DBF
  97.  
  98.     memoFields = 0;
  99.  
  100.     DP.func = GET_DESCRIPTOR_XB;
  101.     DP.handle = dbfHandleOld;
  102.     for (i=1; i <= SDP.fields; i++) {
  103.        DP.fieldNumber = i;
  104.        rez = BULLET(&DP);
  105.        if (rez) {
  106.           printf("GET_DESCRIPTOR_XB failed, rez: %d\n",rez);
  107.           goto ExitNow;
  108.        }
  109.        if (DP.FD.fieldType == 'M') {
  110.           if (memoFields <= MAX_MEMO_FIELDS) {
  111.              memoFO[memoFields] = DP.fieldOffset;
  112.              memoFields++;
  113.           }
  114.           else {
  115.              printf("Too many memo fields defined for program\n");
  116.              goto ExitNow;
  117.           }
  118.        }
  119.     }
  120.     if (memoFields==0) {
  121.        printf("No memo fields in DBF record\n");
  122.        goto ExitNow;
  123.     }
  124.  
  125.     // make new files
  126.  
  127.     CP.func = COPY_DATA_HEADER_XB;
  128.     CP.handle = dbfHandleOld;
  129.     CP.filenamePtr = dbfNewFilenamePtr;
  130.     rez = BULLET(&CP);
  131.     if (rez) {
  132.        printf("Failed COPY_DATA_HEADER, rez: %d\n",rez);
  133.        goto ExitNow;
  134.     }
  135.  
  136.     // open new DBF/DBT files
  137.  
  138.     OP.func = OPEN_DATA_XB;
  139.     OP.filenamePtr = dbfNewFilenamePtr;
  140.     OP.asMode = READWRITE | DENYREADWRITE;
  141.     rez = BULLET(&OP);
  142.     if (rez) {
  143.        printf("DBF open failed, rez: %d\n",rez);
  144.        return 0;
  145.     }
  146.     dbfHandleNew = OP.handle;
  147.  
  148.  
  149.     // allocate DBF record I/O buffer
  150.  
  151.     dbfBufferPtr = malloc(SDP.recordLength);
  152.     if (dbfBufferPtr==NULL) {
  153.        printf("Cannot malloc() dbfBuffer space\n");
  154.        goto ExitNow;
  155.     }
  156.  
  157.     // allocate reusable memo I/O buffer -- size enlarged if necessary
  158.     // for truly large memo data records you may want to modify the program
  159.     // to do memo I/O in chunks
  160.  
  161.     memoMaxSize = MAX_LIKELY_MEMO_SIZE * 1024;
  162.     memoBufferPtr = malloc(memoMaxSize);
  163.     if (memoBufferPtr==NULL) {
  164.        printf("Cannot malloc() memoBuffer space\n");
  165.        goto ExitNow;
  166.     }
  167.  
  168.     recs = SDP.records;
  169.  
  170.     // process:
  171.     // read each data record
  172.     // if deleted skip it and any memo records belonging to it
  173.     // otherwise copy it to new DBF and its memo record(s) to new DBT
  174.     // (PACK_RECORDS_XB is not used in this technique, but could be)
  175.  
  176.     // since it is not necessary to reload all pack members for each
  177.     // operation, often news BULLET calls can be made by updating only
  178.     // one or two of the pack members -- for clarity, this module does
  179.     // setup -all- pack members, even if already setup correctly from
  180.     // previous calls, though may have // commenting them out
  181.  
  182.     MDPo.memoPtr = memoBufferPtr;
  183.     MDPn.memoPtr = memoBufferPtr;
  184.  
  185.     for (i=1; i <= SDP.records; i++) {
  186.  
  187.        // get an old data record
  188.  
  189.        APo.func = GET_RECORD_XB;
  190.        APo.handle = dbfHandleOld;
  191.        APo.recNo = i;
  192.        APo.recPtr = dbfBufferPtr;
  193.        rez = BULLET(&APo);
  194.        if (rez==0) {
  195.  
  196.           // check if not deleted
  197.  
  198.           if (*dbfBufferPtr != '*') {
  199.  
  200.              // for each memo field in old record, get memo copy to new DBT
  201.  
  202.              for (j=0; j < memoFields; j++) {
  203.                 sscanf( (CHAR*)((ULONG)dbfBufferPtr + (ULONG)memoFO[j]), "%10u",&memoNo);
  204.                 if (memoNo) {
  205.  
  206.                    // non-zero memo number means a memo record is out there
  207.  
  208.                    MDPo.func = GET_MEMO_SIZE_XB;
  209.                    MDPo.dbfHandle = dbfHandleOld;
  210.                    MDPo.memoNo = memoNo;
  211.                    rez = BULLET(&MDPo);
  212.                    if (rez==0) {
  213.  
  214.                       // check if current memo buffer needs to be expanded
  215.  
  216.                       if (MDPo.memoBytes > memoMaxSize) {
  217.                          free(memoBufferPtr);
  218.                          memoBufferPtr = malloc(MDPo.memoBytes);
  219.                          if (memoBufferPtr==NULL) {
  220.                             printf("Cannot expand memo buffer to %u bytes\n",MDPo.memoBytes);
  221.                             goto ExitNow;
  222.                          }
  223.                          memoMaxSize = MDPo.memoBytes;
  224.                          MDPo.memoPtr = memoBufferPtr;
  225.                          MDPn.memoPtr = memoBufferPtr;
  226.                       }
  227.  
  228.  
  229.                    // <- offset to left means the structure member is already set correctly
  230.  
  231.                       // get old memo data, all of it in one read
  232.  
  233.                       MDPo.func = GET_MEMO_XB;
  234.                       MDPo.dbfHandle = dbfHandleOld;
  235.                    // MDPo.memoNo = memoNo;
  236.                    // MDPo.memoPtr = memoBufferPtr;
  237.                       MDPo.memoOffset = 0;
  238.                    // MDPo.memoBytes set in previous call
  239.                       rez = BULLET(&MDPo);
  240.                       if (rez==0) {
  241.  
  242.                          // may want to verify MDPo.memoBytes OUT same as requested (IN)
  243.                          // not done here since it should be the same
  244.  
  245.                          // copy old memo data to new memo file
  246.  
  247.                          MDPn.func = ADD_MEMO_XB;
  248.                          MDPn.dbfHandle = dbfHandleNew;
  249.                       // MDPn.memoPtr = memoBufferPtr;
  250.                          MDPn.memoBytes = MDPo.memoBytes;
  251.                          rez = BULLET(&MDPn);
  252.                          if (rez==0) {
  253.  
  254.                             // copy was successful, update DBF record's memo field
  255.                             // with the memo number returned from the add above, in ASCII digits
  256.  
  257.                             // sprintf() to a temp buffer to avoid \0 being added in DBF record
  258.  
  259.                             sprintf(tmpStr,"%10.10u",MDPn.memoNo);
  260.                             //strncpy(dbfBufferPtr + *(memoFO[j]),tmpStr,10); // put memo number in
  261.                             strncpy( (CHAR*)((ULONG)dbfBufferPtr + (ULONG)memoFO[j]),tmpStr,10); // put memo number in
  262.                          }
  263.                          else {
  264.                             printf("ADD_MEMO_XB failed, rez: %d\n",rez);
  265.                             goto ExitNow;
  266.                          }
  267.  
  268.                       }
  269.                       else {
  270.                          printf("GET_MEMO_XB failed, rez: %d\n",rez);
  271.                          goto ExitNow;
  272.                       }
  273.  
  274.                    }
  275.                    else {
  276.                       printf("GET_MEMO_SIZE_XB failed, rez: %d\n",rez);
  277.                       goto ExitNow;
  278.                    }
  279.  
  280.                 } // memoNo was zero for this memo field, continue with...
  281.              } // next memo field in record, if any more
  282.  
  283.              // write out old record to new data file (with new memo field numbers)
  284.  
  285.              APn.func = ADD_RECORD_XB;
  286.              APn.handle = dbfHandleNew;
  287.              APn.recPtr = dbfBufferPtr;
  288.              rez = BULLET(&APn);
  289.              if (rez) {
  290.                 printf("Failed ADD_RECORD_XB, rez: %d\n",rez);
  291.                 goto ExitNow;
  292.              }
  293.  
  294.           }
  295.  
  296.           else {
  297.  
  298.              // track records deleted
  299.  
  300.              deletedRecs++;
  301.  
  302.           }
  303.  
  304.        }
  305.        else {
  306.           printf("GET_RECORD_XB failed, rez: %d\n",rez);
  307.           goto ExitNow;
  308.        }
  309.  
  310.     } // next i (old records)
  311.  } // SDP rez
  312.  
  313. ExitNow:
  314.  
  315.  HP.func = CLOSE_DATA_XB;
  316.  if (dbfHandleOld) {
  317.     HP.handle = dbfHandleOld;
  318.     rez = BULLET(&HP);
  319.  }
  320.  if (dbfHandleNew) {
  321.     HP.handle = dbfHandleNew;
  322.     rez = BULLET(&HP);
  323.  }
  324.  
  325.  if (dbfBufferPtr) free(dbfBufferPtr);
  326.  if (memoBufferPtr) free(memoBufferPtr);
  327.  
  328.  return 0;
  329. }
  330.  
  331.  
  332.  
  333. #ifdef TEST_COMPACT
  334.  
  335.  ////////////////////////////////////////////////
  336.  /////////////////////////////////// test routine
  337.  ////////////////////////////////////////////////
  338.  
  339. #define TEST_RECS_TO_ADD 1000
  340.  
  341. // each test rec uses about 40 bytes in the DBF and about 1KB in the memo
  342. // (since each memo is 512 bytes, and two memoes are written for each DBF record)
  343.  
  344. int main(void) {
  345.  
  346.  void BuildFieldListTest(FIELDDESCTYPE fieldList[]);
  347.  
  348.  typedef struct _TestRecType {
  349.  CHAR tag;
  350.  CHAR ID[9];
  351.  CHAR notes1[10];
  352.  CHAR nada1[6];
  353.  CHAR notes2[10];
  354.  CHAR nada2[4];
  355.  } TestRecType; // 40 bytes
  356.  
  357.  
  358. #pragma pack(1)
  359.  
  360.  ACCESSPACK AP;
  361.  DOSFILEPACK DFP;
  362.  CREATEDATAPACK CDP;
  363.  EXITPACK EP;
  364.  HANDLEPACK HP;
  365.  INITPACK IP;
  366.  MEMODATAPACK MDP;
  367.  OPENPACK OP;
  368.  STATDATAPACK SDP;
  369.  
  370.  FIELDDESCTYPE testFieldList[5];
  371.  TestRecType testRec;
  372.  
  373. #pragma pack()
  374.  
  375.  ULONG dataID=0;
  376.  CHAR dataFilename[] =    ".\\$compact.dbf";
  377.  CHAR memoFilename[] =    ".\\$compact.dbt";
  378.  CHAR dataFilenameNew[] = ".\\$compnew.dbf";
  379.  CHAR memoFilenameNew[] = ".\\$compnew.dbt";
  380.  
  381.  // ### filled in with DBF record number at time memo data created
  382.  // just for later viewing/checking to tell them apart
  383.  
  384.  CHAR memoTestData[]= "######### any memo data, just testing (ONE)";
  385.  CHAR memoTestData2[]="######### any memo data, just testing (TWO)";
  386.  
  387.  CHAR tmpStr[128];
  388.  
  389.  int i;
  390.  LONG rez;
  391.  ULONG deletedRecs=0;
  392.  
  393.  
  394.  IP.func = INIT_XB;
  395.  IP.JFTsize = 20;
  396.  rez = BULLET(&IP);
  397.  if (rez) {
  398.     printf("INIT_XB failed, rez: %d\n",rez);
  399.     goto Abend;
  400.  }
  401.  
  402.  memset(testFieldList,0,sizeof(testFieldList));
  403.  BuildFieldListTest(testFieldList);
  404.  
  405.  testRec.tag = ' ';
  406.  strncpy(testRec.ID,"123456789",9);
  407.  strncpy(testRec.notes1,"          ",10);
  408.  strncpy(testRec.nada1,"nada1",6);
  409.  strncpy(testRec.notes2,"          ",10);
  410.  strncpy(testRec.nada2,"end",4);
  411.  // Delete previous files from any previous run (disregard any error return)
  412.  
  413.  DFP.func = DELETE_FILE_DOS;
  414.  DFP.filenamePtr = dataFilename;
  415.  rez = BULLET(&DFP);
  416.  DFP.filenamePtr = memoFilename;
  417.  rez = BULLET(&DFP);
  418.  DFP.filenamePtr = dataFilenameNew;
  419.  rez = BULLET(&DFP);
  420.  DFP.filenamePtr = memoFilenameNew;
  421.  rez = BULLET(&DFP);
  422.  
  423.  // Create the data files
  424.  
  425.  CDP.func = CREATE_DATA_XB;
  426.  CDP.filenamePtr = dataFilename;
  427.  CDP.noFields = 5;
  428.  CDP.fieldListPtr = testFieldList;
  429.  CDP.fileID = 0x8B;              // bit7&3=1 then also create memo file
  430.  rez = BULLET(&CDP);
  431.  if (rez) {
  432.     printf("Failed TEST data file create.  Err: %d\n",rez);
  433.     goto Abend;
  434.  }
  435.  
  436.  // Open the data file
  437.  
  438.  OP.func = OPEN_DATA_XB;
  439.  OP.filenamePtr = dataFilename;
  440.  OP.asMode = READWRITE | DENYNONE;
  441.  rez = BULLET(&OP);
  442.  if (rez) {
  443.     printf("Failed EMP data file open.  Err: %d\n",rez);
  444.     goto Abend;
  445.  }
  446.  dataID = OP.handle;
  447.  
  448.  // generate memo data and data records
  449.  
  450.  AP.func = ADD_RECORD_XB;
  451.  AP.handle = dataID;
  452.  AP.recPtr = &testRec;
  453.  AP.nextPtr = NULL;
  454.  
  455.  MDP.func = ADD_MEMO_XB;
  456.  MDP.dbfHandle = dataID;
  457.  
  458.  for (i=0;i < TEST_RECS_TO_ADD;i++) {
  459.  
  460.     sprintf(tmpStr,"%9.9u",i);
  461.     strncpy(memoTestData,tmpStr,9);  // overwrite ### with original DBF record number
  462.     strncpy(memoTestData2,tmpStr,9); // just for later viewing (so not all are the same)
  463.  
  464.     MDP.memoPtr = memoTestData;
  465.     MDP.memoBytes = strlen(memoTestData)+1;  // +1 so it stores \0, too
  466.     rez = BULLET(&MDP);
  467.     if (rez!=0) {
  468.        printf("ADD_MEMO_XB #%d failed, err: %d\n",i,MDP.stat);
  469.        goto Abend;
  470.     }
  471.     sprintf(tmpStr,"%10.10u",MDP.memoNo);  // "0000000001" is first memo...
  472.     strncpy(testRec.notes1,tmpStr,10);  // put memo number in
  473.  
  474.     MDP.memoPtr = memoTestData2;
  475.     MDP.memoBytes = strlen(memoTestData2)+1;  // +1 so it stores \0, too
  476.     rez = BULLET(&MDP);
  477.     if (rez!=0) {
  478.        printf("ADD_MEMO_XB #%d failed, err: %d\n",i,MDP.stat);
  479.        goto Abend;
  480.     }
  481.     sprintf(tmpStr,"%10.10u",MDP.memoNo);
  482.     strncpy(testRec.notes2,tmpStr,10);
  483.  
  484.     // AP. members already setup
  485.     rez = BULLET(&AP);
  486.     if (rez) {
  487.        printf("ADD_RECORD_XB failed, rez: %d\n",rez);
  488.        goto Abend;
  489.     }
  490.  }
  491.  
  492.  
  493.  //////////////////////////////// test by deleting every other DBF record
  494.  
  495.  SDP.func = STAT_DATA_XB;
  496.  SDP.handle = dataID;
  497.  rez = BULLET(&SDP);
  498.  if (rez) {
  499.     printf("STAT_DATA_XB failed, rez: %d\n",rez);
  500.     goto Abend;
  501.  }
  502.  printf("There are %u records (each had two memo records)\n",SDP.records);
  503.  
  504.  deletedRecs = 0;
  505.  AP.func = DELETE_RECORD_XB;  // -mark- odd records as deleted
  506.  AP.handle = dataID;
  507.  for (i=1; i <= SDP.records; i++) {
  508.     if (i & 1) {  // if odd, delete
  509.        AP.recNo = i;
  510.        rez = BULLET(&AP);
  511.        if (rez) {
  512.           printf("DELETE_RECORD_XB failed, rez: %d\n",rez);
  513.           goto Abend;
  514.        }
  515.        deletedRecs++;
  516.     }
  517.  }
  518.  
  519.  printf("%u records were marked as deleted (all odd record numbes)\n",deletedRecs);
  520.  printf("Calling compactDB()...\n");
  521.  
  522.  // file must be closed since compactDB opens it by filename
  523.  
  524.  HP.func = CLOSE_DATA_XB;
  525.  HP.handle = dataID;
  526.  rez = BULLET(&HP);
  527.  if (rez) {
  528.     printf("Failed CLOSE_DATA_XB, rez: %d\n",rez);
  529.     goto Abend;
  530.  }
  531.  
  532.  rez = compactDB(dataFilename, dataFilenameNew);
  533.  printf("rez: %d\n",rez);
  534.  
  535.  // Fatal errors above come straight to here
  536. Abend:
  537.  
  538.  
  539.  EP.func = EXIT_XB;
  540.  BULLET(&EP);
  541.  
  542.  printf("\nPress ENTER to end...");
  543.  gets(tmpStr);
  544.  
  545.  return rez;
  546. }
  547.  
  548.  
  549. //---------------------------------------------
  550. // Init field list items for test data file
  551.  
  552. void BuildFieldListTest(FIELDDESCTYPE fieldList[]) {
  553.  
  554.  strcpy(fieldList[0].fieldName, "ID");
  555.  fieldList[0].fieldType = 'C';
  556.  fieldList[0].fieldLen = 9;
  557.  fieldList[0].fieldDC = 0;
  558.  
  559.  strcpy(fieldList[1].fieldName, "NOTES1");
  560.  fieldList[1].fieldType = 'M';
  561.  fieldList[1].fieldLen = 10;
  562.  fieldList[1].fieldDC = 0;
  563.  
  564.  strcpy(fieldList[2].fieldName, "NADA1");
  565.  fieldList[2].fieldType = 'C';
  566.  fieldList[2].fieldLen = 6;
  567.  fieldList[2].fieldDC = 0;
  568.  
  569.  strcpy(fieldList[3].fieldName, "NOTES2");
  570.  fieldList[3].fieldType = 'M';
  571.  fieldList[3].fieldLen = 10;
  572.  fieldList[3].fieldDC = 0;
  573.  
  574.  strcpy(fieldList[4].fieldName, "NADA2");
  575.  fieldList[4].fieldType = 'C';
  576.  fieldList[4].fieldLen = 4;
  577.  fieldList[4].fieldDC = 0;
  578.  return;
  579. }
  580.  
  581. #endif // end TEST_COMPACT
  582.  
  583.  
  584.  
  585.  
  586.  
  587.  
  588.  
  589.  
  590.  
  591.  
  592.